package routower

import (
	"context"
	"go.uber.org/zap"
	"google.golang.org/protobuf/proto"
	"strconv"
	"strings"
	"sync/atomic"
	"time"
	"wzgames/internal/codec/sfs"
	"wzgames/internal/constant"
	"wzgames/internal/gloader"
	"wzgames/internal/model/roulettep"
	"wzgames/pkg/logger"
)

// initFlews 历史轮次信息，并推送到Cache
func (am *RouManager) initFlews(flews []*roulettep.RouRound) {
	xw := sfs.XWriter{}
	defer xw.Reset()
	xw.WriteSfsArrayPre(constant.KeyRoundsInfo, uint16(len(flews)))
	for _, flew := range flews {
		am.flews.Data = append(am.flews.Data, &roulettep.RouFlew{
			RoundId:  flew.RoundId,
			EndDate:  flew.End,
			RetNum:   flew.RetNum,
			RetColor: flew.RetColor,
			RetGrp:   flew.RetGrp,
		})

		xw.WriteUByte(sfs.SfsObject)
		xw.WriteUShort(uint16(5))
		xw.WriteLongS(constant.KeyRoundId, flew.GetRoundId())
		xw.WriteLongS(constant.KeyEndDate, flew.End)
		xw.WriteStringS(constant.KeyNum, flew.RetNum)
		xw.WriteStringS(constant.KeyColor, flew.RetColor)
		xw.WriteStringS(constant.KeyGrp, flew.RetGrp)

		am.updateFlewToCache(flew)
	}

	am.mu.Lock()
	am.updateFlewsToCache(xw.Data())
	am.mu.Unlock()
}

func (am *RouManager) updateFlewToCache(data *roulettep.RouRound) {
	b, err := proto.Marshal(data)
	if err != nil {
		logger.Error("[RouTower] update flew to cache", zap.Int64("InstID", am.inst.InstID), zap.Error(err))
		return
	}
	var sb strings.Builder
	sb.WriteString(gloader.EnvString())
	sb.WriteString(constant.RouFlewKey)
	sb.WriteString(am.instIdStr)
	ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
	_, err = am.redisC.Get().HSet(ctx, sb.String(), strconv.FormatInt(data.RoundId, 10), b).Result()
	cancel()
	if err != nil {
		logger.Error("[RouTower] update flew to cache fail on HSet", zap.Int64("InstID", am.inst.InstID), zap.Error(err))
	}
}

func (am *RouManager) removeFlewFromCache(roundId int64) {
	var sb strings.Builder
	sb.WriteString(gloader.EnvString())
	sb.WriteString(constant.RouFlewKey)
	sb.WriteString(am.instIdStr)
	ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
	_, err := am.redisC.Get().HDel(ctx, sb.String(), strconv.FormatInt(roundId, 10)).Result()
	cancel()
	if err != nil {
		logger.Error("[RouTower] remove flew from cache fail on HDel", zap.Int64("InstID", am.inst.InstID), zap.Error(err))
	}
}

func (am *RouManager) updateFlewsToCache(data []byte) {
	var sb strings.Builder
	sb.WriteString(constant.RouFlewsKey)
	sb.WriteString(am.instIdStr)
	ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
	_, err := am.redisC.Get().Set(ctx, sb.String(), data, 0).Result()
	cancel()
	if err != nil {
		logger.Error("[RouTower] update flews to cache fail on HSet", zap.Int64("InstID", am.inst.InstID), zap.Error(err))
	}
}

func (am *RouManager) updateFlews(flew *roulettep.RouRound) {
	defer logger.Recover()

	newFlew := &roulettep.RouFlew{
		RoundId:  flew.RoundId,
		EndDate:  flew.End,
		RetNum:   flew.RetNum,
		RetColor: flew.RetColor,
		RetGrp:   flew.RetGrp,
	}
	am.flews.Data = append(am.flews.Data, nil)
	copy(am.flews.Data[1:], am.flews.Data[0:])
	am.flews.Data[0] = newFlew

	l := len(am.flews.Data)
	if l > 10 {
		for i := 10; i < l; i++ {
			if am.flews.Data[i] != nil {
				am.removeFlewFromCache(am.flews.Data[i].RoundId)
			}
		}
		am.flews.Data = am.flews.Data[:l-1]
	}
	am.updateFlewToCache(flew)

	xw := sfs.XWriter{}
	defer xw.Reset()
	xw.WriteSfsArrayPre(constant.KeyRoundsInfo, uint16(len(am.flews.Data)))
	for _, fw := range am.flews.Data {
		xw.WriteUByte(sfs.SfsObject)
		xw.WriteUShort(uint16(5))
		xw.WriteLongS(constant.KeyRoundId, fw.GetRoundId())
		xw.WriteLongS(constant.KeyEndDate, fw.GetEndDate())
		xw.WriteStringS(constant.KeyNum, fw.RetNum)
		xw.WriteStringS(constant.KeyColor, fw.RetColor)
		xw.WriteStringS(constant.KeyGrp, fw.RetGrp)
	}

	am.mu.Lock()
	am.updateFlewsToCache(xw.Data())
	am.mu.Unlock()
}

func (am *RouManager) updateOnlineToCache(args interface{}) {
	var sb strings.Builder
	sb.WriteString(gloader.EnvString())
	sb.WriteString(constant.GameOnlineKey)
	sb.WriteString(am.instIdStr)
	ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
	_, err := am.redisC.Get().Set(ctx, sb.String(), atomic.LoadInt64(&am.onlines), 0).Result()
	cancel()
	if err != nil {
		logger.Error("[RouTower] update online to cache fail on Set", zap.Int64("InstID", am.inst.InstID), zap.Error(err))
	}

	TimerAfter(time.Second*3, am.updateOnlineToCache, nil)
}
